#include "app_plan.h"

#define APP_PALN_LENGTH_OFFSET (0x04)
#define APP_PLAN_DATA_OFFSET (0x08)
#define APP_PLAN_BUFF_SIZE (0x80)
#define APP_PLAN_COUNTS (0x14)

typedef struct
{
    unsigned char year;
    unsigned char month;
    unsigned char day;
    unsigned char wday;
} plan_date_t;

typedef struct
{
    unsigned char hour; // 上次执行小时
    unsigned char mins; // 上次执行分钟
    unsigned char day;  // 上次执行日期
} plan_perv_t;

typedef struct
{
    unsigned char period;
    unsigned char hour;
    unsigned char min;
    unsigned char value;
    unsigned char on;
} plan_obj_t;

typedef struct
{
    unsigned char hour;
    unsigned char mins;
    unsigned char value;
} queue_obj_t;

typedef struct
{
    queue_obj_t queue[20];
    unsigned char count;
} plan_wait_t;

typedef struct
{
    plan_perv_t perv;
    unsigned char plan[USER_FLASH_PLAN_SIZE];
    unsigned char counts;
} app_plan_t;

static app_plan_t sg_plan;
static plan_wait_t sg_wait = {0x00};

static void app_plan_add_to_wait(unsigned char hour, unsigned char mins, unsigned char value);
void app_plan_init(void)
{
    unsigned char buff[USER_FLASH_PLAN_SIZE] = {0x00};

    memset(buff, 0x00, USER_FLASH_PLAN_SIZE);
    memset(sg_plan.plan, 0x00, USER_FLASH_PLAN_SIZE);
    sg_plan.counts = 0x00;

    // read from flash
    drv_flash_read(USER_FLASH_PLAN_ADDR, buff, USER_FLASH_PLAN_SIZE);

    // crc32
    unsigned int _len = (USER_FLASH_PLAN_SIZE - APP_PALN_LENGTH_OFFSET) / 4;
    unsigned int crc = app_sys_crc32_cal((unsigned int *)&buff[APP_PALN_LENGTH_OFFSET], _len);
    unsigned int rcrc = (buff[0] << 24) | (buff[1] << 16) | (buff[2] << 8) | (buff[3]);
    if (rcrc == crc)
    {
        _len = (buff[APP_PALN_LENGTH_OFFSET] << 24) | (buff[APP_PALN_LENGTH_OFFSET + 1] << 16) | (buff[APP_PALN_LENGTH_OFFSET + 2] << 8) | (buff[APP_PALN_LENGTH_OFFSET + 3]);
        if (_len == 0x00 || _len > APP_PLAN_BUFF_SIZE || _len % APP_PLAN_OBJ_LENGTH)
        {
            return;
        }

        sg_plan.counts = _len / APP_PLAN_OBJ_LENGTH;
        if (sg_plan.counts >= 0x00 && sg_plan.counts <= APP_PLAN_COUNTS)
        {
            memcpy(sg_plan.plan, &buff[APP_PLAN_DATA_OFFSET], _len);
        }
        else
        {
            sg_plan.counts = 0x00;
        }
    }

    sg_plan.perv.day = 0xFF;
    sg_plan.perv.hour = 0xFF;
    sg_plan.perv.mins = 0xFF;
}

static void app_plan_modify_save(void)
{
    unsigned char buff[USER_FLASH_PLAN_SIZE] = {0x00};
    memset(buff, 0x00, USER_FLASH_PLAN_SIZE);

    unsigned int _len = 0x00;
    if (sg_plan.counts > 0x00 && sg_plan.counts <= APP_PLAN_COUNTS)
    {
        _len = sg_plan.counts * APP_PLAN_OBJ_LENGTH;
        if (_len > APP_PLAN_BUFF_SIZE || _len % APP_PLAN_OBJ_LENGTH)
        {
            return;
        }
    }

    // packet plan data length
    buff[APP_PALN_LENGTH_OFFSET] = (_len >> 24) & 0xFF;
    buff[APP_PALN_LENGTH_OFFSET + 1] = (_len >> 16) & 0xFF;
    buff[APP_PALN_LENGTH_OFFSET + 2] = (_len >> 8) & 0xFF;
    buff[APP_PALN_LENGTH_OFFSET + 3] = (_len >> 0) & 0xFF;

    // packet paln data
    memcpy(&buff[APP_PLAN_DATA_OFFSET], sg_plan.plan, _len);

    // calculate crc32
    _len = (USER_FLASH_PLAN_SIZE - APP_PALN_LENGTH_OFFSET) / 4;
    unsigned int crc = app_sys_crc32_cal((unsigned int *)&buff[APP_PALN_LENGTH_OFFSET], _len);
    buff[0] = (crc >> 24) & 0xFF;
    buff[1] = (crc >> 16) & 0xFF;
    buff[2] = (crc >> 8) & 0xFF;
    buff[3] = (crc >> 0) & 0xFF;

    // write to flash
    drv_flash_write(USER_FLASH_PLAN_ADDR, buff, USER_FLASH_PLAN_SIZE);
}

// 计划时间已经到达
static unsigned char plan_is_time_arrived(rtc_date_time_t *dat, plan_obj_t *obj)
{
    unsigned char period = 0x00;
    switch (dat->date.wday)
    {
    case 0x07: // 周日
        period = 0x01;
        break;
    case 0x06: // 周六
        period = 0x02;
        break;
    case 0x05: // 周五
        period = 0x04;
        break;
    case 0x04:
        period = 0x08;
        break;
    case 0x03:
        period = 0x10;
        break;
    case 0x02:
        period = 0x20;
        break;
    case 0x01:
        period = 0x40;
        break;
    default:
        break;
    }

    if (period == 0x00)
    {
        return 0x00;
    }

    if ((obj->period & period) == 0)
    {
        return 0x00;
    }

    if (dat->time.hour == obj->hour && dat->time.mins == obj->min)
    {
        return 0x01;
    }

    return 0x00;
}

// 计划关闭
static unsigned char plan_is_closed(plan_obj_t *obj)
{
    if (obj->on == 0x00)
    {
        return 0x01;
    }

    return 0x00;
}

static void paln_set_to_do(plan_obj_t *obj, rtc_date_time_t *dat)
{
    if (sg_plan.perv.hour != obj->hour || sg_plan.perv.mins != obj->min)
    {
        sg_plan.perv.hour = obj->hour;
        sg_plan.perv.mins = obj->min;
        sg_plan.perv.day = dat->date.day;

        if (app_release_is_finished() && sg_wait.count == 0)
        {
            if (sg_alarm_mgr.bat_off == 1)  //周计划需要出粮 电量不足
            {
                return ;
            }
            if (app_dev_is_ok_to_sleep() == 1) //周计划需要出粮 在睡眠
            {
                app_led_toggle(APP_LED_WIFI, APP_TOGGLE_FAST_TIME, 0x00);
                app_wifi_set_on();
                app_set_menu_led(MENU_DC_LOCK);
            }
            app_release_set_value(obj->value);
        }
        else
        {
            // 出粮未完成，加入等待队列
            app_plan_add_to_wait(obj->hour, obj->min, obj->value);
        }
    }
}

static void app_plan_add_to_wait(unsigned char hour, unsigned char mins, unsigned char value)
{
    if (sg_wait.count > APP_PLAN_COUNTS)
    {
        return;
    }

    sg_wait.queue[sg_wait.count].hour = hour;
    sg_wait.queue[sg_wait.count].mins = mins;
    sg_wait.queue[sg_wait.count].value = value;

    sg_wait.count += 1;
}

static void app_plan_wait_carry_out(void)
{
    if (sg_wait.count == 0)
    {
        return;
    }

    if (!app_release_is_finished())
    {
        return;
    }
    else
    {
        app_release_set_value(sg_wait.queue[0].value);
    }

    for (unsigned char i = 1; i < sg_wait.count; i++)
    {
        sg_wait.queue[i - 1].hour = sg_wait.queue[i].hour;
        sg_wait.queue[i - 1].mins = sg_wait.queue[i].mins;
        sg_wait.queue[i - 1].value = sg_wait.queue[i].value;
    }

    sg_wait.count -= 1;
}

void app_plan_task(void)
{
    rtc_date_time_t datetime;
    drv_rtc_GetTime(&datetime);

    app_plan_wait_carry_out();

    if (datetime.date.day != sg_plan.perv.day)
    {
        sg_plan.perv.hour = 0xFF;
        sg_plan.perv.mins = 0xFF;
    }

    unsigned short offset = 0;
    unsigned short length = sg_plan.counts * APP_PLAN_OBJ_LENGTH;
    while (offset < length)
    {
        plan_obj_t obj = {0x00};
        obj.period = sg_plan.plan[offset++];
        obj.hour = sg_plan.plan[offset++];
        obj.min = sg_plan.plan[offset++];
        obj.value = sg_plan.plan[offset++];
        obj.on = sg_plan.plan[offset++];

        if (0x01 == plan_is_closed(&obj))
        {
            continue;
        }

        if (0x00 == plan_is_time_arrived(&datetime, &obj))
        {
            continue;
        }
        paln_set_to_do(&obj, &datetime);
    }
}

void app_plan_set(const unsigned char *data, unsigned short length)
{
    ret_if_true(length > APP_PLAN_BUFF_SIZE)

        memset(sg_plan.plan, 0x00, APP_PLAN_BUFF_SIZE);
    sg_plan.counts = 0x00;

    if ((length >= APP_PLAN_OBJ_LENGTH) && (0x00 == length % APP_PLAN_OBJ_LENGTH))
    {
        sg_plan.counts = length / APP_PLAN_OBJ_LENGTH;
        memcpy(sg_plan.plan, data, length);
    }

    app_plan_modify_save();
}
// 40 13 1B 01 01
// 20 13 1C 01 01
unsigned char app_plan_get(unsigned char *buffer, unsigned int size)
{
    unsigned int _len = 0x00;

    if (sg_plan.counts == 0x00)
    {
        _len = 0x02;
        unsigned char temp[2] = {0x00};
        memcpy(buffer, temp, _len);
    }
    else
    {
        _len = sg_plan.counts * APP_PLAN_OBJ_LENGTH;
        _len = _len > size ? size : _len;
        memcpy(buffer, sg_plan.plan, _len);
    }

    return (_len & 0xFF);
}

void app_plan_clear_to_flash(void)
{
    memset((void *)&sg_plan, 0x00, sizeof(app_plan_t));

    app_plan_modify_save();
}